home *** CD-ROM | disk | FTP | other *** search
/ Aminet 3 / Aminet 3 - July 1994.iso / Aminet / misc / unix / tracker_4_3.lzh / tracker / Amiga / ui.c < prev   
Encoding:
C/C++ Source or Header  |  1994-02-13  |  18.4 KB  |  780 lines

  1. /* amiga/ui.c 
  2.     vi:se ts=3 sw=3:
  3.  */
  4.  
  5. /* $Id: ui.c,v 1.19 1994/01/09 23:25:16 Espie Exp Espie $
  6.  * $Log: ui.c,v $
  7.  * Revision 1.19  1994/01/09  23:25:16  Espie
  8.  * Last bug fix.
  9.  *
  10.  * Revision 1.18  1994/01/09  17:38:28  Espie
  11.  * Generalized open.c.
  12.  *
  13.  * Revision 1.17  1994/01/09  04:49:18  Espie
  14.  * File requester !
  15.  *
  16.  * Revision 1.16  1994/01/08  20:26:07  Espie
  17.  * Added pause gadget.
  18.  *
  19.  * Revision 1.15  1994/01/08  19:45:29  Espie
  20.  * Uncentralized event handling using event management functions.
  21.  *
  22.  * Revision 1.13  1994/01/07  15:08:54  Espie
  23.  * Changed name to ui_win, added Show gadget.
  24.  *
  25.  * Revision 1.12  1994/01/06  22:37:26  Espie
  26.  * better coding for gadgets.
  27.  * Nasty bug with info: did not close the file properly.
  28.  *
  29.  * Revision 1.11  1994/01/05  19:24:08  Espie
  30.  * Fully working asynchronous interface.
  31.  *
  32.  * Revision 1.10  1994/01/05  16:48:58  Espie
  33.  * User feedback.
  34.  *
  35.  * Revision 1.9  1994/01/05  16:12:06  Espie
  36.  * Problem with output ? Still to fix !
  37.  *
  38.  * Revision 1.8  1994/01/05  14:55:38  Espie
  39.  * *** empty log message ***
  40.  *
  41.  * Revision 1.7  1994/01/05  02:01:00  Espie
  42.  * Added missing autoinit
  43.  *
  44.  * Revision 1.6  1994/01/04  15:44:03  Espie
  45.  * Removed some typecasts.
  46.  *
  47.  * Revision 1.5  1993/12/28  14:03:53  Espie
  48.  * info facility.
  49.  * scroll post-synchronized output.
  50.  * notice.
  51.  *
  52.  * Revision 1.4  1993/12/27  04:11:01  Espie
  53.  * Cursor handling.
  54.  *
  55.  * Revision 1.3  1993/12/27  02:35:02  Espie
  56.  * Used of discard_buffer for premature ending.
  57.  *
  58.  * Revision 1.2  1993/12/26  22:48:18  Espie
  59.  * Mostly working.
  60.  * Just dies with a guru.
  61.  * Plus timing problems at start.
  62.  *
  63.  * Revision 1.1  1993/12/26  18:54:21  Espie
  64.  * Initial revision
  65.  *
  66.  */
  67.  
  68. #include <stdio.h>
  69. #include <stdlib.h>
  70. #include <string.h>
  71.  
  72. #include <proto/intuition.h>
  73. #include <proto/gadtools.h>
  74. #include <proto/exec.h>
  75. #include <dos/dos.h>
  76.  
  77. #include "defs.h"
  78. #include "extern.h"
  79. #include "amiga/amiga.h"
  80. #include "tags.h"
  81. #include "prefs.h"
  82.  
  83. ID("$Id: ui.c,v 1.19 1994/01/09 23:25:16 Espie Exp Espie $")
  84. LOCAL void init_ui(void);
  85. LOCAL void do_set_current(VALUE current);
  86. LOCAL void handle_ui_window(GENERIC nothing);
  87.  
  88. LOCAL void (*INIT)(void) = init_ui;
  89.  
  90.  
  91. /* These two variables memorize where we actually are in
  92.  * the current song
  93.  */
  94. LOCAL int current_pattern;
  95.  
  96. #define SMALL_DELAY 3         /* in seconds */
  97.  
  98. /* And these when stuff last changed */
  99. LOCAL ULONG pattern_change_seconds, pattern_change_micros,
  100. song_change_seconds, song_change_micros;
  101.  
  102.  
  103. /* The basic user interface (a simple window) */
  104.  
  105. struct IntuitionBase *IntuitionBase = 0;
  106. struct GfxBase *GfxBase = 0;
  107. LOCAL struct Library *GadtoolsBase = 0;
  108. struct Window *ui_win;
  109.  
  110. /* for asynchronous easy requests */
  111. LOCAL struct Window *notice_win = 0;
  112.  
  113.    /* for computing length */
  114. LOCAL struct IntuiText it;
  115.  
  116. #define SHIFT 4
  117. #define SPACEX 10
  118. #define SPACEY 4
  119.  
  120. #define WINDOW_TITLE "Experiment IV "        /* the space in case the font is italic */
  121.  
  122. LOCAL struct NewGadget template =
  123.    {
  124.    0, 0,
  125.    0, 0,
  126.    NULL,
  127.    0,
  128.    0,       /* gadget ID */
  129.    0,
  130.    NULL,
  131.    NULL
  132.    };
  133.  
  134. LOCAL struct NewMenu menu_template[] =
  135.    {
  136.    {NM_TITLE,  "Project",        0, 0, 0, 0},
  137.    {NM_ITEM,   "Load song...",   0, 0, 0, (void *)4},
  138.    {NM_ITEM,   "About...",       0, 0, 0, (void *)1},
  139.    {NM_ITEM,   "Quit",           0, 0, 0, (void *)2},
  140.    {NM_TITLE,  "Settings",       0, 0, 0, 0},
  141.    {NM_ITEM,   "PAL",            0, CHECKIT | CHECKED, 6, (void *)50},
  142.    {NM_ITEM,   "NTSC",           0, CHECKIT, 5, (void *)60},
  143.    {NM_ITEM,   "Custom",         0, CHECKIT, 3, (void *)3},
  144.    {NM_END,    0,                0, 0, 0, 0}
  145.    };
  146.  
  147. LOCAL struct Menu *menu;
  148.  
  149. LOCAL APTR vi;
  150. LOCAL struct Screen *pub = 0;
  151. LOCAL struct Gadget *glist, *title_gad, *pattern_gad, *total_gad;
  152.  
  153. /* we precisely have seven gadgets */
  154. #define MAX_GADGET 6
  155.  
  156. /* all labelled with strings */
  157. LOCAL char *label[MAX_GADGET + 1] =
  158.    {
  159.    "|<",
  160.    "<<",
  161.    "//",
  162.    ">>", 
  163.    ">|",
  164.    "?",
  165.    "000",
  166.    };
  167.  
  168. #define G_NEXT 4
  169. #define G_RESTART_PREVIOUS 0
  170. #define G_REWIND 1
  171. #define G_FF 3
  172. #define G_SHOW 5
  173. #define G_PAUSE 2
  174.  
  175. /* the special structure to display lines 
  176.  *    
  177.  */
  178. LOCAL struct MinList scrolls;
  179. struct scroll_line
  180.    {
  181.    struct MinNode node;
  182.    char buffer[80];
  183.    };
  184.  
  185. LOCAL struct ext_message *restart_msg = NULL;
  186.  
  187. LOCAL void cleanup_ui()
  188.    {
  189.    if (restart_msg)
  190.       {
  191.       send(restart_msg, TYPE_UNPAUSE);
  192.       restart_msg = 0;
  193.       }
  194.    if (ui_win)
  195.       {
  196.       remove_signal_handler(ui_win->UserPort->mp_SigBit);
  197.       SetWindowTitles(ui_win, 0, 0);
  198.       ClearMenuStrip(ui_win);
  199.       CloseWindow(ui_win);
  200.       ui_win = 0;
  201.       }
  202.    if (menu)
  203.       FreeMenus(menu);
  204.    if (glist)
  205.       FreeGadgets(glist);
  206.    if (vi)
  207.       FreeVisualInfo(vi);
  208.    if (pub)
  209.       UnlockPubScreen(NULL, pub);
  210.    while (notice_win)
  211.       await_events();
  212.    if (GfxBase)
  213.       CloseLibrary(GfxBase);
  214.    if (IntuitionBase)
  215.       CloseLibrary(IntuitionBase);
  216.    if (GadtoolsBase)
  217.       CloseLibrary(GadtoolsBase);
  218.    }
  219.    
  220.  
  221. LOCAL void init_ui(void)
  222.    {
  223.    struct Gadget *gad;
  224.    int i;
  225.    int max_width;
  226.    int width, height;
  227.    UWORD zoom[4];
  228.  
  229.    NewList(&scrolls);
  230.    at_end(cleanup_ui);
  231.    IntuitionBase = OpenLibrary("intuition.library", 37);
  232.    if (!IntuitionBase)
  233.       end_all("No Intuition");
  234.    GadtoolsBase = OpenLibrary("gadtools.library", 37);
  235.    if (!GadtoolsBase)
  236.       end_all("No gadtools");
  237.    GfxBase = OpenLibrary("graphics.library", 37);
  238.    if (!GfxBase)
  239.       end_all("No graphics");
  240.    pub = LockPubScreen(NULL);
  241.    if (!pub)
  242.       end_all("No pubscreen");
  243.    vi = GetVisualInfo(pub, TAG_END);
  244.    if (!vi)
  245.       end_all("No VI");
  246.    menu = CreateMenus(menu_template, TAG_END);
  247.    if (!menu)
  248.       end_all("No menus");
  249.    if (!LayoutMenus(menu, vi, TAG_END))
  250.       end_all("Menus badly formed");
  251.       
  252.       /* now to create the gadgets */
  253.       /* Compute max width/height according to the font */
  254.    it.ITextFont = pub->Font;
  255.    template.ng_TextAttr = pub->Font;
  256.    
  257.    max_width = 0;
  258.    for (i = 0; i < MAX_GADGET + 1; i++)
  259.       {
  260.       it.IText = label[i];  
  261.       width = IntuiTextLength(&it);
  262.       if (width > max_width)
  263.          max_width = width;
  264.       }
  265.     
  266.    max_width += SPACEX;
  267.    template.ng_Width = max_width;     
  268.    template.ng_Height = pub->Font->ta_YSize + SPACEY;
  269.  
  270.       /* set up Top/Left Edge of initial gadget according to Wbar */
  271.    template.ng_TopEdge = pub->WBorTop + 1 + 2 * template.ng_Height + SHIFT;
  272.    template.ng_LeftEdge = pub->WBorLeft + SHIFT; 
  273.  
  274.    gad = CreateContext(&glist);
  275.    if (!gad)
  276.       end_all("No context");
  277.    template.ng_VisualInfo = vi;
  278.       /* lay out gadgets */
  279.    for (i = 0; i < MAX_GADGET; i++)
  280.       {
  281.       template.ng_GadgetText = label[i];
  282.       gad = CreateGadget(BUTTON_KIND, gad, &template, TAG_END);
  283.       if (!gad)
  284.          end_all("Bad gadget");
  285.       template.ng_LeftEdge += template.ng_Width + SHIFT;
  286.       template.ng_GadgetID++;
  287.       }
  288.    width = template.ng_LeftEdge + pub->WBorRight;
  289.    height = template.ng_TopEdge + template.ng_Height + pub->WBorBottom + SHIFT;
  290.  
  291.       /* zoom box */
  292.    zoom[0] = ~0;
  293.    zoom[1] = ~0;
  294.    zoom[2] = width;
  295.    zoom[3] = pub->WBorTop + 1 + pub->Font->ta_YSize;
  296.  
  297.       /* title gadget */     
  298.    template.ng_GadgetText = "";
  299.    template.ng_Width = width - SHIFT - pub->WBorLeft - pub->WBorRight;
  300.    template.ng_Width -= 2 * max_width;
  301.    template.ng_TopEdge = pub->WBorTop + 1 + template.ng_Height + SHIFT;
  302.    template.ng_LeftEdge = SHIFT + pub->WBorLeft;
  303.    title_gad = gad = CreateGadget(TEXT_KIND, gad, &template, TAG_END);
  304.    if (!gad)
  305.       end_all("Bad gadget");
  306.       
  307.       /* pattern gadget */
  308.    template.ng_LeftEdge += template.ng_Width + SHIFT;
  309.    template.ng_Width = max_width;
  310.    pattern_gad = gad = CreateGadget(NUMBER_KIND, gad, &template, TAG_END);
  311.    if (!gad)
  312.       end_all("Bad gadget");
  313.  
  314.       /* total pattern */
  315.    template.ng_GadgetText = "/";
  316.    template.ng_LeftEdge += template.ng_Width + SHIFT;
  317.    total_gad = gad = CreateGadget(NUMBER_KIND, gad, &template, TAG_END);
  318.    if (!gad)
  319.       end_all("Bad gadget");
  320.  
  321.    ui_win = OpenWindowTags(NULL, 
  322.       WA_Title, WINDOW_TITLE,
  323.       WA_Width, width,
  324.       WA_Height, height,
  325.       WA_AutoAdjust, TRUE,
  326.       WA_MouseQueue, 35,   /* we can't always answer messages */
  327.       WA_DepthGadget, TRUE,
  328.       WA_CloseGadget, TRUE,
  329.       WA_DragBar, TRUE,
  330.       WA_Zoom, zoom,
  331.       WA_Gadgets, glist,
  332.       WA_IDCMP, IDCMP_CLOSEWINDOW | BUTTONIDCMP | TEXTIDCMP | 
  333.                 IDCMP_REFRESHWINDOW | IDCMP_MENUPICK,
  334.       TAG_DONE, 0);
  335.    if (!ui_win)
  336.       end_all("No window");
  337.    GT_RefreshWindow(ui_win, NULL);  
  338.    SetMenuStrip(ui_win, menu);
  339.  
  340.    install_signal_handler(ui_win->UserPort->mp_SigBit, handle_ui_window, 0);
  341.    
  342.    /* build up scroll buffer stuff */
  343.    }
  344.  
  345.  
  346. /* Max number of input messages we can remember */
  347. #define MAX_INPUT 10
  348. LOCAL struct tag result[MAX_INPUT +1];
  349. LOCAL int i;
  350.  
  351.  
  352. LOCAL void handle_ui_window(GENERIC nothing)
  353.    {
  354.    struct IntuiMessage *msg;
  355.    UWORD number;
  356.    struct MenuItem *item;
  357.    int id;
  358.    VALUE temp;
  359.    
  360.    while((msg = GT_GetIMsg(ui_win->UserPort)) && i < MAX_INPUT)
  361.       switch(msg->Class)
  362.          {
  363.       case IDCMP_CLOSEWINDOW:
  364.          GT_ReplyIMsg(msg);
  365.          set_break();
  366.          break;
  367.       case IDCMP_MENUPICK:
  368.          number = msg->Code;
  369.          while (number != MENUNULL)
  370.             {
  371.             item = ItemAddress(menu, msg->Code);
  372.             switch((int)GTMENUITEM_USERDATA(item))
  373.                {
  374.             case 1:
  375.                notice(
  376. "Tracker 4.0\n\
  377.       by Marc Espie (Marc.Espie@ens.fr)\n\n\
  378. This is a giftware program\n\
  379. If you want, you can send me some money\n\
  380. My address is:\n\
  381.       Espie Marc\n\
  382.       60 rue du 4 septembre\n\
  383.       87100 Limoges\n\
  384.       France\n\n\
  385. For the most recent version:\n\
  386.       ftp Aminet or nic.funet.fi");
  387.                break;
  388.             case 2:
  389.                GT_ReplyIMsg(msg);
  390.                set_break();
  391.                break;
  392.             case 4:
  393.                launch_requester();
  394.                break;
  395.             case 50:
  396.             case 60:
  397.                result[i].type = UI_SET_BPM;
  398.                result[i++].data.scalar = (int)GTMENUITEM_USERDATA(item);
  399.                break;
  400.             default:
  401.                break;
  402.                }
  403.             number = item->NextSelect;
  404.             }
  405.          GT_ReplyIMsg(msg);
  406.          break;
  407.       case IDCMP_REFRESHWINDOW:
  408.          GT_ReplyIMsg(msg);
  409.          GT_BeginRefresh(ui_win);
  410.          GT_EndRefresh(ui_win, TRUE);
  411.          break;
  412.       case IDCMP_GADGETUP:
  413.          id = ((struct Gadget *)msg->IAddress)->GadgetID;
  414.          switch(id)
  415.             {
  416.          case G_NEXT:
  417.             result[i++].type = UI_NEXT_SONG;
  418.             break;
  419.          case G_RESTART_PREVIOUS:
  420.             if (msg->Seconds < song_change_seconds + SMALL_DELAY ||
  421.                 (msg->Seconds == song_change_seconds + SMALL_DELAY && 
  422.                 msg->Micros <= song_change_micros) )
  423.                 {
  424.                 result[i++].type = UI_PREVIOUS_SONG;
  425.                 break;
  426.                 }
  427.             else
  428.                {
  429.                result[i++].type = UI_RESTART;
  430.                song_change_seconds = msg->Seconds;
  431.                song_change_micros = msg->Micros;
  432.                }
  433.             break;
  434.          case G_REWIND:
  435.             result[i].type = UI_JUMP_TO_PATTERN;
  436.             result[i].data.scalar = current_pattern;
  437.             if (msg->Seconds < pattern_change_seconds + SMALL_DELAY ||
  438.                 (msg->Seconds == pattern_change_seconds + SMALL_DELAY && 
  439.                 msg->Micros <= pattern_change_micros) )
  440.                 result[i].data.scalar--;
  441.                    /* give some immediate feedback to the user */
  442.             temp.scalar = result[i].data.scalar;
  443.             do_set_current(temp);
  444.                 i++;
  445.             break;
  446.          case G_FF:
  447.             result[i].type = UI_JUMP_TO_PATTERN;
  448.             result[i].data.scalar = current_pattern + 1;
  449.                   /* give some immediate feedback to the user */
  450.             temp.scalar = result[i].data.scalar;
  451.             do_set_current(temp);
  452.             i++;
  453.             break;
  454.          case G_SHOW:
  455.             set_pref_scalar(PREF_SHOW, TRUE);
  456.             break;
  457.          case G_PAUSE:
  458.             if (restart_msg)
  459.                {
  460.                send(restart_msg, TYPE_UNPAUSE);
  461.                restart_msg = 0;
  462.                }
  463.             else
  464.                {
  465.                struct ext_message *msg;
  466.                
  467.                msg = obtain_message();
  468.                restart_msg = obtain_message();
  469.                send(msg, TYPE_PAUSE);
  470.                }
  471.             }
  472.          GT_ReplyIMsg(msg);
  473.          break;
  474.       default:
  475.          GT_ReplyIMsg(msg);
  476.          }
  477.    }
  478.  
  479.  
  480. void requested_file(struct amiganame *name)
  481.    {
  482.    result[i].data.pointer = name;
  483.    result[i++].type = UI_LOAD_SONG;
  484.    }
  485.  
  486.    
  487. struct tag *get_ui()
  488.    {
  489.  
  490.    INIT_ONCE
  491.  
  492.    if (checkbrk())
  493.       result[i++].type = UI_QUIT;
  494.    
  495.    result[i].type = TAG_END;
  496.  
  497.    i = 0;
  498.  
  499.    return result;
  500.    }
  501.  
  502.  
  503. void song_title(char *s)
  504.    {
  505.    static char title[25];
  506.  
  507.    INIT_ONCE;
  508.  
  509.    strncpy(title, s, 25);
  510.    if (ui_win)
  511.       GT_SetGadgetAttrs(title_gad, ui_win, 0, GTTX_Text, title, TAG_END);
  512.    /* stamp the time we changed the song */
  513.    CurrentTime(&song_change_seconds, &song_change_micros);
  514.    }
  515.  
  516. void status(char *s)   
  517.    {
  518.    INIT_ONCE;
  519.    
  520.    SetWindowTitles(ui_win, s ? s : WINDOW_TITLE, -1);
  521.    }
  522.  
  523. /***
  524.  ***
  525.  ***    Scrolling line handling: 
  526.  ***        note this is totally asynchronous and uses the TYPE_DO_SYNC
  527.  ***        message for synchronizing with songs that are really played
  528.  ***/
  529.  
  530.  
  531. /* look at client.c/audio.c for the use of inhibit_output with respect
  532.  * to discard_buffer
  533.  */
  534. unsigned int inhibit_output = 0;
  535.  
  536. LOCAL struct scroll_line *scroll_buffer = 0;
  537.  
  538. char *new_scroll(void)
  539.    {
  540.    char *s;
  541.        /* need some temporary storage in case everything is full */
  542.     LOCAL char buffer[80];
  543.    INIT_ONCE;
  544.                            /* check for a scroll line available */
  545.    scroll_buffer = RemHead(&scrolls);
  546.    if (!scroll_buffer)     /* none available ? allocate one on the fly */
  547.       scroll_buffer = malloc(sizeof(struct scroll_line));
  548.    if (scroll_buffer)
  549.       s = scroll_buffer->buffer;
  550.    else                    /* still none ? use static buffer */
  551.       s = buffer;
  552.    strcpy(s, "                                                       ");
  553.    return s;
  554.    }
  555.  
  556. /* The actual hook that does all the printing */
  557. LOCAL void do_scroll(VALUE p)  
  558.    {
  559.    struct scroll_line *s = p.pointer;
  560.     
  561.     if (inhibit_output == 0 && get_pref_scalar(PREF_SHOW))
  562.         {
  563.         add_scroller(s->buffer);
  564.       }
  565.     AddTail(&scrolls, s);
  566.    }
  567.  
  568. void scroll()
  569.    {
  570.    struct ext_message *msg;
  571.    
  572.    if (scroll_buffer)        /* did we obtain a scroll line ? */
  573.       {                    /* then set up to scroll it */
  574.       msg = obtain_message();
  575.       msg->data.hook.func = do_scroll;
  576.       msg->data.hook.p.pointer = scroll_buffer;
  577.       send(msg, TYPE_SYNC_DO);
  578.       }
  579.    scroll_buffer = 0;
  580.    }
  581.  
  582. /* hook to change current pattern */
  583. LOCAL void do_set_current(VALUE p)
  584.    {
  585.    if (!inhibit_output)
  586.       {
  587.       INIT_ONCE;
  588.       if (ui_win)
  589.          GT_SetGadgetAttrs(pattern_gad, ui_win, 0, GTNM_Number, p.scalar, TAG_END);
  590.       }
  591.    current_pattern = p.scalar;
  592.    /* stamp the time we changed the pattern */
  593.    CurrentTime(&pattern_change_seconds, &pattern_change_micros);
  594.    }
  595.  
  596. /* hook to change current pattern total */
  597. LOCAL void do_set_total(VALUE p)
  598.     {
  599.     INIT_ONCE;
  600.    if (ui_win)
  601.       GT_SetGadgetAttrs(total_gad, ui_win, 0, GTNM_Number, p.scalar, TAG_END);
  602.     }
  603.  
  604. void display_pattern(int current, int total)
  605.    {
  606.    struct ext_message *msg;
  607.  
  608.    INIT_ONCE 
  609.  
  610.    msg = obtain_message();
  611.     msg->data.hook.func = do_set_total;
  612.    msg->data.hook.p.scalar = total;
  613.    send(msg, TYPE_SYNC_DO);
  614.  
  615.    msg = obtain_message();
  616.     msg->data.hook.func = do_set_current;
  617.    msg->data.hook.p.scalar = current;
  618.    send(msg, TYPE_SYNC_DO);
  619.    }
  620.  
  621.  
  622.  
  623. /***
  624.  ***
  625.  ***        Info window handling
  626.  ***
  627.  ***/
  628.  
  629. /* We chose the easy way: outputting everything in a console window
  630.  * on the fly. An interesting improvement would be to buffer everything
  631.  * and open the window with the right size afterwards
  632.  */
  633. struct handle
  634.    {
  635.    FILE *file;
  636.    int linecount;
  637.    int maxlength;
  638.    int currentlength;
  639.    };
  640.  
  641. #define H(h, field)  ( ((struct handle *)h)->field )
  642.  
  643. void *begin_info(char *title)
  644.    {
  645.    struct handle *new;
  646.    
  647.    char buffer[50];
  648.    
  649.    new = malloc(sizeof(struct handle));
  650.    if (!new)
  651.       return 0;
  652.    sprintf(buffer, "CON:////%s/auto/close/wait", title);
  653.    new->file=fopen(buffer, "w");
  654.    if (!new->file)
  655.       {
  656.       free(new);
  657.       return 0;
  658.       }
  659.    new->linecount = 0;
  660.    new->maxlength = 0;
  661.    new->currentlength = 0;
  662.    return new;
  663.    }
  664.  
  665. void infos(void *handle, char *s)
  666.    {
  667.    if (handle)
  668.       {
  669.       fprintf( H(handle,file), s);
  670.       H(handle, currentlength) += strlen(s);
  671.       }
  672.    }
  673.  
  674. void info(void *handle, char *line)
  675.    {
  676.    infos(handle, line);
  677.    if (handle)
  678.       {
  679.       fputc('\n', H(handle, file));
  680.       if ( H(handle, currentlength) > H(handle, maxlength) )
  681.          H(handle, maxlength) = H(handle, currentlength);
  682.       H(handle, linecount)++;
  683.       }
  684.    }
  685.  
  686. void end_info(void *handle)
  687.    {
  688.    if (handle)
  689.       {
  690.       
  691.       fclose(H(handle, file));
  692.       free(handle);
  693.       }
  694.    }
  695.  
  696.  
  697.  
  698. /***
  699.  ***
  700.  ***    notice() pseudo-system call.
  701.  ***    mostly used to report errors
  702.  ***
  703.  ***    The only difficulty comes from the fact
  704.  ***    that we may be called under any kind of environment
  705.  ***
  706.  ***/
  707.  
  708. #ifdef USE_ARQ
  709. #include "arq.h"
  710. /* arq 1.78 doesn't notice BuildEasyRequest().
  711.    I need to contact Martin Laubach about it
  712.        FidoNet: 2:310/3.14 
  713.        Usenet:  mjl@alison.at (home) 
  714.                                mjl@auto.tuwien.ac.at (work) 
  715.                 {cbmvax!cbmehq,mcsun!tuvie}!cbmvie!alison!mjl 
  716.  
  717.        Peter, the graphics and animation wizard, can be reached
  718.      2:310/42 in FidoNet.  
  719.  */
  720.  
  721. LOCAL struct ExtEasyStruct es =
  722.    {
  723.    0,
  724.    0,
  725.    ARQ_ID_INFO,
  726.    0,
  727.    ARQ_MAGIC,
  728.    0, 0, 0,
  729.    sizeof(struct EasyStruct),
  730.    0,
  731.    "Notice\xa0",
  732.    NULL,
  733.    "Proceed"
  734.    };
  735. LOCAL struct EasyStruct *esp = & (es.Easy);
  736. #else
  737. LOCAL struct EasyStruct es = 
  738.    {
  739.    sizeof(struct EasyStruct),
  740.    0,
  741.    "Notice",
  742.    NULL,
  743.    "Proceed"
  744.    };
  745. LOCAL struct EasyStruct *esp = &es;
  746. #endif
  747.  
  748.  
  749. void handle_notice(struct Window *w)
  750.    {
  751.    if (SysReqHandler(w, 0, FALSE) != -2)
  752.       {
  753.       remove_signal_handler(w->UserPort->mp_SigBit);
  754.       FreeSysRequest(w);
  755.       notice_win = 0;
  756.       }
  757.    }
  758.  
  759. void notice(char *s)
  760.    {
  761.    INIT_ONCE;
  762.    
  763.    if (!IntuitionBase)
  764.       {
  765.       fprintf(stderr, s);
  766.       fputc('\n', stderr);
  767.       }
  768.    else
  769.       {
  770.          /* wait for previous notice to go away */
  771.       while (notice_win)
  772.          await_events();
  773.       
  774.       esp->es_TextFormat = s;
  775.       notice_win = BuildEasyRequest(0, esp, NULL, NULL);
  776.       install_signal_handler(notice_win->UserPort->mp_SigBit, handle_notice, notice_win);
  777.       }
  778.    }
  779.  
  780.